package com.github.bryx.workflow.util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Data
@EqualsAndHashCode(callSuper=true)
public class JsonObjectWrapper extends JSONObject{
	
	private static final long serialVersionUID = -6492832730144047609L;
	
	private JSONObject object;
	
	private JsonObjectWrapper(JSONObject object){
		this.object = object;
	}
	
	public static JsonObjectWrapper of(JSONObject object) {
		return new JsonObjectWrapper(object);
	}
	
	@Override
	public String getString(String key) {
		return this.getValue(String.class, key);
	}
	
	@Override
	public @Nullable Date getDate(String key) {
		return this.getValue(Date.class, key);
	}
	
	private @Nullable <T> T getValue(JSONObject jsonObject, Class<T> elementClass, String... keys){
		String key = keys[0];
		if (keys.length == 1) {
			if (jsonObject != null && jsonObject.containsKey(key)) {
				return jsonObject.getObject(key, elementClass);
			}
		}else {
			if (jsonObject.get(key) != null && jsonObject.containsKey(key)) {
				if (jsonObject.get(key) instanceof JSONObject) {
					return this.getValue((JSONObject)jsonObject.get(key), elementClass, Arrays.copyOfRange(keys, 1, keys.length));
				}else if(jsonObject.get(key) instanceof String){
					try {
						Object json = JSONObject.parse((String)jsonObject.get(key));
						return this.getValue((JSONObject)json, elementClass, Arrays.copyOfRange(keys, 1, keys.length));
					}catch(Exception e){
						return null;
					}
				}
			}
		}
		return null;
	}
	
	private @NonNull <T> List<T> getValueAsArray(JSONObject jsonObject, Class<T> elementClass, String... keys){
		String key = keys[0];
		if (keys.length == 1) {
			if (jsonObject != null && jsonObject.containsKey(key) && (jsonObject.get(key) instanceof JSONArray)) {
				List<T> result = Lists.newArrayList();
				((JSONArray)jsonObject.get(key)).forEach(item->{
					result.add(elementClass.cast(item));
				});
				return result;
			}
		}else {
			if (jsonObject.get(key) != null && jsonObject.containsKey(key) && (jsonObject.get(key) instanceof JSONObject)) {
				return this.getValueAsArray((JSONObject)jsonObject.get(key), elementClass, Arrays.copyOfRange(keys, 1, keys.length));
			}
		}
		return Lists.newArrayList();
	}
	
	private Map<String, Object> getValueAsMap(JSONObject jsonObject, String... keys){
		String key = keys[0];
		if (keys.length == 1) {
			if (jsonObject != null && jsonObject.containsKey(key) && (jsonObject.get(key) instanceof JSONObject)) {
				Map<String, Object> result = Maps.newHashMap();
				result.putAll((JSONObject)jsonObject.get(key));
				return result;
			}
		}else {
			if (jsonObject.get(key) != null && jsonObject.containsKey(key) && (jsonObject.get(key) instanceof JSONObject)) {
				return this.getValueAsMap((JSONObject)jsonObject.get(key), Arrays.copyOfRange(keys, 1, keys.length));
			}
		}
		return Maps.newHashMap();
		
	}
	
	public @Nullable <T> T getValue(Class<T> elementClass, String... keys) {
		return this.getValue(this.object, elementClass, keys);
	}
	
	public @NonNull <T> List<T> getValueAsArray(Class<T> elementClass, String... keys) {
		return this.getValueAsArray(this.object, elementClass, keys);
	}
	
	public @NonNull Map<String, Object> getValueAsMap(String... keys) {
		return this.getValueAsMap(object, keys);
	}
	
}
